import { BASE_CFG_JSON, getPackageJson } from "@ap/util/packageJson";
import { Client } from "./request";
import { auth, nPost, ua, userData } from "@ap/util/request";
import { FolderReader, Scripts } from "./translate";
import {
  loginNullMsg,
  mapToolNullMsg,
  showErrorMessage,
  showInformationMessage,
  showProgress,
} from "@ap/util/vscode/message";
import { isFileExists, read, ReadMode } from "@ap/util/fs";
import { tempPath } from "@ap/util/tempPath";
import { parallel } from "@ap/util/data";
import { arenaErrorMsg } from "@ap/util/vscode/errorMsg";
import { OutputChannel, OutputWrapper } from "@ap/util/vscode/output";
import AppConfig from "./config";
import getFilePath from "@ap/util/vscode/getFilePath";
import * as vscode from "vscode";
import type { PackageConfig } from "@ap/types/config";
import {
  ConfigurationItem,
  ConfigurationKey,
} from "@ap/util/vscode/setInsConfig";
import path from "path";
import { CreateStatusBar } from "@ap/init";
import { getPathAndFileName2Config } from "../util/vscode/getPathAndFileName2Config";
import { isDevelopment } from "./isDevelopment";

function solveIfNotAbsolute(temp: string, base?: string) {
  if (tempPath) {
    return path.isAbsolute(temp) ? temp : path.resolve(base ?? tempPath, temp);
  }
  return temp;
}
export const buildMsg =
  "坐和放宽，项目正编译打包成捆绑文件并上传至神岛地图中...";
/**
 * 构建并上传代码。
 * 此函数负责协调整个构建和上传过程，包括读取配置、准备脚本、打包代码和上传打包后的文件。
 * 它使用了进度条来反馈过程中的状态。
 */
export async function buildNUpload() {
  if (CreateStatusBar.buildToolObj.tooltip === buildMsg) {
    OutputChannel.getInstance().show();
    return "已经有项目正在构建，请稍后再试";
  }
  const buildPath = getFilePath();
  if (!tempPath) {
    showErrorMessage(arenaErrorMsg.OPEN_WORKSPACE);
    return arenaErrorMsg.OPEN_WORKSPACE;
  }
  CreateStatusBar.buildToolObj.text = "$(loading~spin) 项目构建中...";
  CreateStatusBar.buildToolObj.tooltip = buildMsg;
  const outputWrapper = new OutputWrapper(OutputChannel.getInstance());
  outputWrapper.info("Start building and uploading task.");
  let compileMsg = "";
  // 显示一个进度条，用于指示构建和上传的总体进度。
  await showProgress("构建Arena-Js项目", async (progress) => {
    try {
      // 记录开始时间，用于计算整个过程的耗时。
      const time = Date.now();
      // 分别获取文件和地图的配置信息。
      const arenaProFileConfig =
        await getPackageJson<PackageConfig["file"]>("file");
      const arenaProMapConfig =
        await getPackageJson<PackageConfig["map"]>("map");
      const arenaProNPMPackagesConfig =
        await getPackageJson<PackageConfig["npmPackage"]>("npmPackage");

      // 校验配置信息是否完整，如果不完整则显示错误消息并终止操作。
      if (!arenaProFileConfig || !arenaProMapConfig) {
        stopCompileBuildToolObj();
        showErrorMessage(arenaErrorMsg.PROFILE_AND_MAP_INFORMATION);
        return arenaErrorMsg.PROFILE_AND_MAP_INFORMATION;
      }
      if (!arenaProFileConfig.typescript) {
        stopCompileBuildToolObj();
        showErrorMessage(arenaErrorMsg.CONFIGURE_TYPESCRIPT_INFORMATION);

        compileMsg = arenaErrorMsg.CONFIGURE_TYPESCRIPT_INFORMATION;
        return compileMsg;
      }

      // 根据配置信息初始化客户端对象，用于后续的上传操作。
      const client = new Client({
        token: auth,
        mapId: arenaProMapConfig.id,
        folderReader: FolderReader.getInstance(),
        ua: vscode.workspace
          .getConfiguration(ConfigurationItem.USER_CENTER_TOOL)
          .get<string>(ConfigurationKey.USER_AGNET),
      });

      // 根据配置信息初始化脚本对象，用于代码的打包。
      const data = buildPath;
      if (!data) {
        stopCompileBuildToolObj();
        compileMsg = arenaErrorMsg.OPEN_WORKSPACE;
        return compileMsg;
      }
      Scripts.setRootDir(data);

      const pkgPath = path.parse(getFilePath(BASE_CFG_JSON) || "").dir;
      if (!pkgPath) {
        showErrorMessage(arenaErrorMsg.CONFIGURE_PACKAGE_JSON);
        stopCompileBuildToolObj();
        compileMsg = arenaErrorMsg.CONFIGURE_PACKAGE_JSON;
        return compileMsg;
      }
      const pathAndFileName2Config = await getPathAndFileName2Config(true);
      // const basePath = solveIfNotAbsolute(arenaProFileConfig.typescript.server.base, pkgPath);
      // path.resolve(basePath, "webpack.config.js")
      const scripts = Scripts.createScripts({
        server: {
          npmPackages: arenaProNPMPackagesConfig || undefined,
          base: pathAndFileName2Config?.server.base ?? "./server",
          entry: pathAndFileName2Config?.server.path || "src/App.ts",
          development: !!isDevelopment(true, arenaProFileConfig),
          webpack:
            arenaProFileConfig.typescript.server.webpack ||
            "./server/webpack.config.js",
          output:
            pathAndFileName2Config?.server.fileName || "_server_bundle.js",
          esbuild: arenaProFileConfig.typescript.server.esbuild || false,
        },
        client: {
          npmPackages: arenaProNPMPackagesConfig || undefined,
          base: pathAndFileName2Config?.client.base ?? "./client",
          entry: pathAndFileName2Config?.client.path || "src/clientApp.ts",
          development: !!isDevelopment(false, arenaProFileConfig),
          webpack:
            arenaProFileConfig.typescript.client.webpack ||
            "./client/webpack.config.js",
          output:
            pathAndFileName2Config?.client.fileName || "_client_bundle.js",
          esbuild: arenaProFileConfig.typescript.client.esbuild || false,
        },
      });

      // 安装依赖
      progress.report({ message: "正在检测/安装依赖" });
      await Scripts.installRequirements();

      // 报告进度，表示正在准备阶段。
      progress.report({ message: "正在编译" });
      // 编译TS项目
      const [serverDist, clientDist] = await Promise.all([
        scripts.server.compile(),
        scripts.client.compile(),
      ]);

      // 报告进度，表示正在打包阶段。
      progress.report({ message: "正在打包" });
      // 打包服务器和客户端的代码，根据配置决定是否并行打包。
      let paths: [string, string] = await parallel<[string, string], string>(
        [
          () =>
            scripts.server.pack(
              path.resolve(
                pathAndFileName2Config?.server.base || "./server",
                pathAndFileName2Config?.server.path || "src/App.ts",
              ),
              path.resolve(
                serverDist,
                pathAndFileName2Config?.server.fileName ||
                  AppConfig.extension.compile.typescript.outputBundle.server,
              ),
            ),
          () =>
            scripts.client.pack(
              path.resolve(
                pathAndFileName2Config?.client.base || "./client",
                pathAndFileName2Config?.client.path || "src/clientApp.ts",
              ),

              path.resolve(
                clientDist,
                pathAndFileName2Config?.client.fileName ||
                  AppConfig.extension.compile.typescript.outputBundle.client,
              ),
            ),
        ],
        AppConfig.extension.compile.typescript.allowParallel,
      );

      console.log(paths[0], paths[1]);
      async function checkAndUploadFile(
        path: string,
      ): Promise<{ error: boolean; msg: string }> {
        try {
          if (!path || !(await isFileExists(path))) {
            stopCompileBuildToolObj();
            showErrorMessage(
              arenaErrorMsg.DESCRIPTION_FAILED_TO_PACKAGE_WEBPACK +
                "，原因：捆绑js文件不存在。",
            );
            return {
              error: true,
              msg:
                arenaErrorMsg.DESCRIPTION_FAILED_TO_PACKAGE_WEBPACK +
                "，原因：捆绑js文件不存在。",
            };
          }
        } catch (error) {
          stopCompileBuildToolObj();
          showErrorMessage(
            arenaErrorMsg.DESCRIPTION_FAILED_TO_PACKAGE_WEBPACK +
              "，原因：" +
              error,
          );
          outputWrapper.error("Error checking file existence:" + error);
          return {
            error: true,
            msg:
              arenaErrorMsg.DESCRIPTION_FAILED_TO_PACKAGE_WEBPACK +
              "，原因：" +
              error,
          };
        }
        return {
          error: false,
          msg: "",
        };
      }

      if (arenaProNPMPackagesConfig === "server") {
        const res = await checkAndUploadFile(paths[0]);
        if (res.error) {
          compileMsg = res.msg;
          return compileMsg;
        }
      } else if (arenaProNPMPackagesConfig === "client") {
        const res = await checkAndUploadFile(paths[1]);
        if (res.error) {
          compileMsg = res.msg;
          return compileMsg;
        }
      } else {
        const res1 = await checkAndUploadFile(paths[0]);
        const res2 = await checkAndUploadFile(paths[1]);
        if (res1.error || res2.error) {
          compileMsg = res1.msg || res2.msg;
          return compileMsg;
        }
      }

      if (arenaProFileConfig.typescript.jsUpdate === false) {
        const msg =
          "构建完成! 暂不上传至地图。用时: " +
          ((Date.now() - time) / 1000).toFixed(2) +
          "s";
        showInformationMessage(msg);
        outputWrapper.success("Finish building task.");
        stopCompileBuildToolObj();
        compileMsg = msg;
        return compileMsg;
      }
      // 检查打包结果是否存在，如果不存在则显示错误消息。
      if (!auth) {
        const msg =
          "构建成功，上传前请先登录神岛账号。用时: " +
          ((Date.now() - time) / 1000).toFixed(2) +
          "s";
        showInformationMessage(msg);
        outputWrapper.success("Finish building task.");
        stopCompileBuildToolObj();
        compileMsg = msg;
        return compileMsg;
      }

      if (!arenaProMapConfig.id || !arenaProMapConfig.editHash) {
        const msg =
          "构建成功，上传前请先配置地图信息。用时: " +
          ((Date.now() - time) / 1000).toFixed(2) +
          "s";
        showInformationMessage(msg);
        outputWrapper.success("Finish building task.");
        stopCompileBuildToolObj();
        compileMsg = msg;
        return compileMsg;
      }

      // 报告进度，表示正在上传阶段。
      progress.report({ message: "正在上传" });
      const { READ } = ReadMode;
      const contents = await Promise.all<[any, any]>(
        paths.map((path) => read(path, { mode: READ, raw: true })) as [
          Promise<any>,
          Promise<any>,
        ],
      );

      // 上传文件内容。
      await client.updateAllFiles(
        arenaProNPMPackagesConfig,
        contents,
        pathAndFileName2Config?.server.fileName,
        pathAndFileName2Config?.client.fileName,
      );
      // 完成上传，显示成功消息和耗时。
      const msg =
        "构建并上传完成! 用时: " +
        ((Date.now() - time) / 1000).toFixed(2) +
        "s";
      showInformationMessage(msg);
      outputWrapper.success("Finish building and uploading task.");
      stopCompileBuildToolObj();
      if (userData && userData.data) {
        nPost({
          url: "https://box3lab-api.fanhat.cn/dao3lab/arenapro_building_count",
          data: {
            type: 1,
            userid: userData.data.userId,
            name: userData.data.nickname,
            mapid: arenaProMapConfig.id,
            buildingTime: ((Date.now() - time) / 1000).toFixed(2) + "s",
          },
        });
      }
      compileMsg = msg;
      return compileMsg;
    } catch (err) {
      // 如果过程中出现错误，显示错误消息。
      console.log(err);
      showErrorMessage(
        err instanceof Error ? err.message || "" : "出现错误: " + err,
      );
      outputWrapper.error(
        "An error occurred during building and uploading task: " + err,
      );
      stopCompileBuildToolObj();
      compileMsg =
        err instanceof Error ? err.message || "" : "出现错误: " + err;
      return compileMsg;
    }
  });
  return compileMsg;
}

export function stopCompileBuildToolObj() {
  CreateStatusBar.buildToolObj.text = "$(arena-up-icon) 完整构建项目";
  const toip = new vscode.MarkdownString(
    CreateStatusBar.buildToolObj_text,
    true,
  );
  toip.isTrusted = true;
  CreateStatusBar.buildToolObj.tooltip = toip;
}

export async function upLoadJsFile(
  isServer: boolean,
  filePath: string,
  fileName: string,
) {
  const { READ } = ReadMode;
  const contents = await read(filePath, { mode: READ, raw: true });
  if (!contents) {
    return;
  }
  const arenaProMapConfig = await getPackageJson<PackageConfig["map"]>("map");
  // 校验配置信息是否完整，如果不完整则显示错误消息并终止操作。
  if (!arenaProMapConfig) {
    showErrorMessage(arenaErrorMsg.PROFILE_AND_MAP_INFORMATION);
    return;
  }
  // 根据配置信息初始化客户端对象，用于后续的上传操作。
  const client = new Client({
    token: auth,
    mapId: arenaProMapConfig.id,
    folderReader: FolderReader.getInstance(),
    ua: ua,
  });

  const res = await client.updateFile(
    isServer ? 0 : 1,
    contents,
    fileName,
    fileName,
  );
  return res;
}
